/**
 * Chaser
 * target chasing utility
 *
 * @author		Allen Chou
 * @version		1.0.2 (last update: Mar 21 2008)
 * @link		http://cjcat2266.deviantart.com
 */

package idv.cjcat.utils.chaser {
	import flash.display.DisplayObject;
	import flash.display.Stage;
	import flash.events.*;
	import flash.errors.IllegalOperationError;
	import flash.geom.Point;
	import idv.cjcat.events.ChaserEvent;

	public class Chaser {
		
		public  static var     chaserInfo:EventDispatcher = new EventDispatcher();
		
		private static var         _stage:Stage;
		private static var     _initiated:Boolean         = false;
		
		//added "locked mode" in version 1.0.2
		private static var _attDOChaserSP:Array           = new Array();
		private static var _attDOChaserDP:Array           = new Array();	//different parent
		private static var    _attMChaser:Array           = new Array();
		private static var _dirDOChaserSP:Array           = new Array();
		private static var _dirDOChaserDP:Array           = new Array();	//different parent
		private static var    _dirMChaser:Array           = new Array();
		private static var _locDOChaserSP:Array           = new Array();
		private static var _locDOChaserDP:Array           = new Array();    //different parent
		private static var    _locMChaser:Array           = new Array();
		
		public static function init(stage:Stage):void {
			_stage = stage;
			_initiated = true;
		}
		
		/**
		 * makes chaser start chasing its target
		 * @param	chaser	the DisplayObject to be chasing its target.
		 * @param	target  specifies the DisplayObject to be chased; null means the mouse cursor is the target.
		 * @param	mode    in direct mode the chaser's velocity always points to its target; in ATTRACTIVE mode the chaser's acceleration always points to its target.
		 * @param	speed   how fast the chaser is chasing its target; speed value should be between zero and one.
		 * @param   damping affects the chaser only in ATTRACTIVE mode; the higher the damping, the slower the chasing; damping value should be between zero and one.
		 */
		public static function addChaser(chaser:DisplayObject, target:DisplayObject = null, mode:String = "attractive", speed:Number = 0.2, damping:Number = 0.3):void {
			if (!_initiated) throw IllegalOperationError("call Chaser.init(stage) before using any Chaser operation");
			
			if (chaser == null) throw IllegalOperationError("chaser:DisplayObject must not be null");
			_stage.addEventListener(Event.ENTER_FRAME, update);
			
			removeChaser(chaser);
			
			if (speed   < 0) speed   = 0.00001;
			if (speed   > 1) speed   = 1;
			if (damping < 0) damping = 0;
			if (damping > 1) damping = 1;
			
			if ((target != null) && (target.parent != null) && (chaser.parent != null)) {
				switch (mode) {
					case ChaserMode.ATTRACTIVE:
						if (chaser.parent == target.parent) {
							_attDOChaserSP.push({chaserObj:chaser, chaseTarget:target, chaseSpeed:speed, vx:0, vy:0, dampRatio:1 - damping, reached:false});
						} else {
							_attDOChaserDP.push({chaserObj:chaser, chaseTarget:target, chaseSpeed:speed, vx:0, vy:0, dampRatio:1 - damping, reached:false});
						}
						break;
					case ChaserMode.DIRECT:
						if (chaser.parent == target.parent) {
							_dirDOChaserSP.push({chaserObj:chaser, chaseTarget:target, chaseSpeed:speed, reached:false});
						} else {
							_dirDOChaserDP.push({chaserObj:chaser, chaseTarget:target, chaseSpeed:speed, reached:false});
						}
						break;
					case ChaserMode.LOCKED:
						if (chaser.parent == target.parent) {
							_locDOChaserSP.push({chaserObj:chaser, chaseTarget:target});
						} else {
							_locDOChaserDP.push({chaserObj:chaser, chaseTarget:target});
						}
						break;
				}
			} else if ((target == null) && (chaser.parent != null)) {
				switch (mode) {
					case ChaserMode.ATTRACTIVE:
						_attMChaser.push({chaserObj:chaser, chaseSpeed:speed, vx:0, vy:0, dampRatio:1 - damping, reached:false});
						break;
					case ChaserMode.DIRECT:
						_dirMChaser.push({chaserObj:chaser, chaseSpeed:speed, reached:false});
						break;
					case ChaserMode.LOCKED:
						_locMChaser.push({chaserObj:chaser});
						break;
				}
			}
		}
		
		public static function removeChaser(chaser:DisplayObject):void {
			//_attDOChaserSP
			var i:int;
			if (_attDOChaserSP.length > 0) {
				for (i = 0; i < _attDOChaserSP.length; i++) {
					if (_attDOChaserSP[i].chaser == chaser) _attDOChaserSP.splice(i, 1);
				}
			}
			
			//_attDOChaserDP
			if (_attDOChaserDP.length > 0) {
				for (i = 0; i < _attDOChaserDP.length; i++) {
					if (_attDOChaserDP[i].chaser == chaser) _attDOChaserSP.splice(i, 1);
				}
			}
			
			//_attMChaser
			if (_attMChaser.length > 0) {
				for (i = 0; i < _attMChaser.length; i++) {
					if ( _attMChaser[i].chaser == chaser)  _attMChaser.splice(i, 1);
				}
			}
			
			//_dirDOChaserSP
			if (_dirDOChaserSP.length > 0) {
				for (i = 0; i < _dirDOChaserSP.length; i++) {
					if (_dirDOChaserSP[i].chaser == chaser) _dirDOChaserSP.splice(i, 1);
				}
			}
			
			//_dirDOChaserDP
			if (_dirDOChaserDP.length > 0) {
				for (i = 0; i < _dirDOChaserDP.length; i++) {
					if (_dirDOChaserDP[i].chaser == chaser) _dirDOChaserDP.splice(i, 1);
				}
			}
			
			//_dirMChaser
			if (_dirMChaser.length > 0) {
				for (i = 0; i < _dirMChaser.length; i++){
					if ( _dirMChaser[i].chaser == chaser)  _dirMChaser.splice(i, 1);
				}
			}
			
			//_locDOChaserSP
			if (_locDOChaserSP.length > 0) {
				for (i = 0; i < _locDOChaserSP.length; i++){
					if ( _locDOChaserSP[i].chaser == chaser)  _locDOChaserSP.splice(i, 1);
				}
			}
			
			//_locDOChaserDP
			if (_locDOChaserDP.length > 0) {
				for (i = 0; i < _locDOChaserDP.length; i++){
					if ( _locDOChaserDP[i].chaser == chaser)  _locDOChaserDP.splice(i, 1);
				}
			}
			
			//_locMChaser
			if (_locMChaser.length > 0) {
				for (i = 0; i < _locMChaser.length; i++){
					if ( _locMChaser[i].chaser == chaser)  _locMChaser.splice(i, 1);
				}
			}
			
		}
		
		public static function removeAllChasers(mouseChaser:Boolean = false, mode:String = "attractive"):void {
			if (mouseChaser) {
				switch (mode) {
					case ChaserMode.ATTRACTIVE:
						_attDOChaserSP = new Array();
						_attDOChaserDP = new Array();
						break;
					case ChaserMode.DIRECT:
						_dirDOChaserSP = new Array();
						_dirDOChaserDP = new Array();
						break;
					case ChaserMode.LOCKED:
						_locDOChaserSP = new Array();
						_locDOChaserDP = new Array();
						break;
				}
			} else {
				switch (mode) {
					case ChaserMode.ATTRACTIVE:
						_attMChaser = new Array();
						break;
					case ChaserMode.DIRECT:
						_dirMChaser = new Array();
						break;
					case ChaserMode.LOCKED:
						_locMChaser = new Array();
						break;
				}
			}
		}
		
		private static var tempObj:Object;
		private static var tempPoint:Point;
		private static var dx:Number;
		private static var dy:Number;
		private static function update(e:Event):void {
			
			//_attDOChaserSP
			if (_attDOChaserSP.length > 0) {
				var i:int;
				for (i = 0; i < _attDOChaserSP.length; i++) {
					tempObj = _attDOChaserSP[i];
					dx = tempObj.chaseTarget.x - tempObj.chaserObj.x;
					dy = tempObj.chaseTarget.y - tempObj.chaserObj.y;
					if ((Math.pow(dx, 2) + Math.pow(dy, 2) < 1) && (Math.pow(tempObj.vx , 2) + Math.pow(tempObj.vy, 2) < 1)) {
						tempObj.vx = tempObj.vy = 0;
						tempObj.chaserObj.x = tempObj.chaseTarget.x;
						tempObj.chaserObj.y = tempObj.chaseTarget.y;
						if (!tempObj.reached) {
							chaserInfo.dispatchEvent(new ChaserEvent(ChaserEvent.REACHED, tempObj.chaserObj, tempObj.chaseTarget));
							tempObj.reached = true;
						}
						continue;
					} else {
						tempObj.reached = false;
					}
					
					tempObj.vx += tempObj.chaseSpeed * dx;
					tempObj.vy += tempObj.chaseSpeed * dy;
					tempObj.vx *= tempObj.dampRatio;
					tempObj.vy *= tempObj.dampRatio;
					tempObj.chaserObj.x += tempObj.vx;
					tempObj.chaserObj.y += tempObj.vy;
				}
			}
			
			//_attDOChaserDP
			if (_attDOChaserDP.length > 0) {
				for (i = 0; i < _attDOChaserDP.length; i++) {
					tempObj = _attDOChaserDP[i];
					tempPoint = tempObj.chaserObj.parent.globalToLocal(tempObj.chaseTarget.localToGlobal(new Point(0, 0)));
					dx = tempPoint.x - tempObj.chaserObj.x;
					dy = tempPoint.y - tempObj.chaserObj.y;
					if ((Math.pow(dx, 2) + Math.pow(dy, 2) < 1) && (Math.pow(tempObj.vx , 2) + Math.pow(tempObj.vy, 2) < 1)) {
						tempObj.vx = tempObj.vy = 0;
						tempObj.chaserObj.x = tempPoint.x;
						tempObj.chaserObj.y = tempPoint.y;
						if (!tempObj.reached) {
							chaserInfo.dispatchEvent(new ChaserEvent(ChaserEvent.REACHED, tempObj.chaserObj, tempObj.chaseTarget));
							tempObj.reached = true;
						}
						continue;
					} else {
						tempObj.reached = false;
					}
					
					tempObj.vx += tempObj.chaseSpeed * dx;
					tempObj.vy += tempObj.chaseSpeed * dy;
					tempObj.vx *= tempObj.dampRatio;
					tempObj.vy *= tempObj.dampRatio;
					tempObj.chaserObj.x += tempObj.vx;
					tempObj.chaserObj.y += tempObj.vy;
				}
			}
			
			//_attMChaser
			if (_attMChaser.length > 0) {
				for (i = 0; i <  _attMChaser.length; i++) {
					tempObj = _attMChaser[i];
					dx = tempObj.chaserObj.parent.mouseX - tempObj.chaserObj.x;
					dy = tempObj.chaserObj.parent.mouseY - tempObj.chaserObj.y;
					if ((Math.pow(dx, 2) + Math.pow(dy, 2) < 1) && (Math.pow(tempObj.vx , 2) + Math.pow(tempObj.vy, 2) < 1)) {
						tempObj.vx = tempObj.vy = 0;
						tempObj.chaserObj.x = tempObj.chaserObj.parent.mouseX;
						tempObj.chaserObj.y = tempObj.chaserObj.parent.mouseY;
						if (!tempObj.reached) {
							chaserInfo.dispatchEvent(new ChaserEvent(ChaserEvent.REACHED, tempObj.chaserObj, "MouseCursor"));
							tempObj.reached = true;
						}
						continue;
					} else {
						tempObj.reached = false;
					}
					
					tempObj.vx += tempObj.chaseSpeed * dx;
					tempObj.vy += tempObj.chaseSpeed * dy;
					tempObj.vx *= tempObj.dampRatio;
					tempObj.vy *= tempObj.dampRatio;
					tempObj.chaserObj.x += tempObj.vx;
					tempObj.chaserObj.y += tempObj.vy;
				}
			}
			
			//_dirDOChaserSP
			
			if (_dirDOChaserSP.length > 0) {
				for (i = 0; i < _dirDOChaserSP.length; i++) {
					tempObj = _dirDOChaserSP[i];
					dx = tempObj.chaseTarget.x - tempObj.chaserObj.x;
					dy = tempObj.chaseTarget.y - tempObj.chaserObj.y;
					if (Math.pow(dx, 2) + Math.pow(dy, 2) < 1) {
						tempObj.chaserObj.x = tempObj.chaseTarget.x;
						tempObj.chaserObj.y = tempObj.chaseTarget.y;
						if (!tempObj.reached) {
							chaserInfo.dispatchEvent(new ChaserEvent(ChaserEvent.REACHED, tempObj.chaserObj, tempObj.chaseTarget));
							tempObj.reached = true;
						}
						continue;
					} else {
						tempObj.reached = false;
					}
					
					tempObj.chaserObj.x += tempObj.chaseSpeed * dx;
					tempObj.chaserObj.y += tempObj.chaseSpeed * dy;
				}
			}
			
			//_dirDOChaserDP
			if (_dirDOChaserDP.length > 0) {
				for (i = 0; i < _dirDOChaserDP.length; i++) {
					tempObj = _dirDOChaserDP[i];
					tempPoint = tempObj.chaserObj.parent.globalToLocal(tempObj.chaseTarget.localToGlobal(new Point(0, 0)));
					dx = tempPoint.x - tempObj.chaserObj.x;
					dy = tempPoint.y - tempObj.chaserObj.y;
					if (Math.pow(dx, 2) + Math.pow(dy, 2) < 1) {
						tempObj.chaserObj.x = tempPoint.x;
						tempObj.chaserObj.y = tempPoint.y;
						if (!tempObj.reached) {
							chaserInfo.dispatchEvent(new ChaserEvent(ChaserEvent.REACHED, tempObj.chaserObj, tempObj.chaseTarget));
							tempObj.reached = true;
						}
						continue;
					} else {
						tempObj.reached = false;
					}
					
					tempObj.chaserObj.x += tempObj.chaseSpeed * dx;
					tempObj.chaserObj.y += tempObj.chaseSpeed * dy;
				}
			}
			
			//_dirMChaser
			if (_dirMChaser.length > 0) {
				for (i = 0; i <  _dirMChaser.length; i++){
					tempObj = _dirMChaser[i];
					dx = tempObj.chaserObj.parent.mouseX - tempObj.chaserObj.x;
					dy = tempObj.chaserObj.parent.mouseY - tempObj.chaserObj.y;
					if (Math.pow(dx, 2) + Math.pow(dy, 2) < 1) {
						tempObj.chaserObj.x = tempObj.chaserObj.parent.mouseX;
						tempObj.chaserObj.y = tempObj.chaserObj.parent.mouseY;
						if (!tempObj.reached ) {
							chaserInfo.dispatchEvent(new ChaserEvent(ChaserEvent.REACHED, tempObj.chaserObj, "MouseCursor"));
							tempObj.reached = true;
						}
					} else {
						tempObj.reached = false;
					}
					
					tempObj.chaserObj.x += tempObj.chaseSpeed * dx;
					tempObj.chaserObj.y += tempObj.chaseSpeed * dy;
				}
			}
			
			//_locDOChaserSP
			if (_locDOChaserSP.length > 0) {
				for (i = 0; i < _locDOChaserSP.length; i++) {
					tempObj = _locDOChaserSP[i];
					tempObj.chaserObj.x = tempObj.chaseTarget.x;
					tempObj.chaserObj.y = tempObj.chaseTarget.y;
				}
			}
			
			//_locDOChaserDP
			if (_locDOChaserDP.length > 0) {
				for (i = 0; i < _locDOChaserDP.length; i++) {
					tempObj = _locDOChaserDP[i];
					tempPoint = tempObj.chaserObj.parent.globalToLocal(tempObj.chaseTarget.localToGlobal(new Point(0, 0)));
					tempObj.chaserObj.x = tempPoint.x;
					tempObj.chaserObj.y = tempPoint.y;
				}
			}
			
			//_locMChaser
			if (_locMChaser.length > 0) {
				for (i = 0; i < _locMChaser.length; i++) {
					tempObj = _locMChaser[i];
					tempObj.chaserObj.x = tempObj.chaserObj.parent.mouseX;
					tempObj.chaserObj.y = tempObj.chaserObj.parent.mouseY;
				}
			}
		}
		
	}

}